﻿using AcademySystem.DAL;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data.SqlClient;
using System.Linq;
using Telerik.OpenAccess;
using System.Web.Security;
using System.Collections.Generic;

namespace AcademySystem.DAL.Tests
{
    [TestClass()]
    public class AcademySystemDALTest
    {
        public const string PRODUCTION_DB_NAME = "AcademySystem";
        public const string TEST_DB_NAME = "AcademySystemDBTest";
        public const string CLONE_CONNECTION_STRING =
            @"Data Source=.\SQLEXPRESS;Initial Catalog=master;Integrated Security=True;MultipleActiveResultSets=True";
        private static string databaseDirectory;
        private static List<object> userIDs;

        [ClassInitialize]
        public static void InitTestDatabase(TestContext testContext)
        {
            databaseDirectory = testContext.TestDeploymentDir;
            CloneDatabase();
            PopulateTestingData();
        }

        [ClassCleanup]
        public static void CleanupTestDatabase()
        {
            string sqlDropTestDatabase = String.Format(@"
				USE master

				IF EXISTS(SELECT * FROM sys.sysdatabases where name='{0}')
				BEGIN
					ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
					DROP DATABASE {0}
				END
			", TEST_DB_NAME);

            SqlConnection dbConnection = new SqlConnection(CLONE_CONNECTION_STRING);
            dbConnection.Open();
            using (dbConnection)
            {
                SqlCommand dropDBCmd = new SqlCommand(sqlDropTestDatabase, dbConnection);
                dropDBCmd.ExecuteNonQuery();
            }
        }

        [TestMethod()]
        public void GetCourseTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };

                    context.Add(course);
                    context.SaveChanges();

                    Course currentCourse = AcademySystemDAO.GetCourse(course.CourseID);
                    Assert.IsTrue(AreEqual(course, currentCourse));

                    context.Delete(course);
                    context.SaveChanges();

                    currentCourse = AcademySystemDAO.GetCourse(course.CourseID);
                    Assert.IsNull(currentCourse);
                }
            }
        }

        [TestMethod()]
        public void AddCourseTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    string title = "title" + i;
                    Guid creatorUserID = (Guid)userIDs[i];
                    DateTime startDate = DateTime.Now;
                    DateTime endDate = DateTime.Now.AddDays(i);
                    string information = "this is " + i + "th test course";

                    Course course = AcademySystemDAO.AddCourse(title, creatorUserID, startDate, endDate, information);
                    Course currentCourse = context.Courses.SingleOrDefault(c => c.CourseID == course.CourseID);

                    Assert.IsTrue(AreEqual(course, currentCourse));

                    context.Delete(currentCourse);
                    context.SaveChanges();

                    currentCourse = context.Courses.SingleOrDefault(c => c.CourseID == course.CourseID);

                    Assert.IsNull(currentCourse);
                }
            }
        }

        [TestMethod()]
        public void UpdateCourseTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };

                    context.Add(course);
                    context.SaveChanges();

                    course.StartDate = DateTime.Now.AddMonths(2);
                    course.EndDate = DateTime.Now.AddMonths(5);
                    course.Information = "This course is updated";
                   
                    bool isUpdated = AcademySystemDAO.UpdateCourse(course.CourseID, 
                        course.StartDate, course.EndDate, course.Information);
                    Assert.IsTrue(isUpdated);

                    Course updatedCourse = context.Courses.SingleOrDefault(c => c.CourseID == course.CourseID);
                    Assert.IsTrue(AreEqual(course, updatedCourse));

                    context.Delete(course);
                    context.SaveChanges();
                    isUpdated = AcademySystemDAO.UpdateCourse(course.CourseID,
                        course.StartDate, course.EndDate, "This course i updated again");
                    Assert.IsFalse(isUpdated);
                }
            }
        }

        [TestMethod()]
        public void DeleteCourseTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };
                    context.Add(course);
                    context.SaveChanges();

                    bool isDeleted = AcademySystemDAO.DeleteCourse(course.CourseID);
                    Assert.IsTrue(isDeleted);

                    isDeleted = AcademySystemDAO.DeleteCourse(course.CourseID);
                    Assert.IsFalse(isDeleted);
                }
            }
        }

        [TestMethod()]
        public void GetSubscriptionTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };
                    context.Add(course);
                    context.SaveChanges();

                    Subscription subscription = new Subscription()
                    {
                        CourseID = course.CourseID,
                        SubscriborUserID = (Guid)userIDs[i],
                        SubscriptionDate = DateTime.Now
                    };
                    context.Add(subscription);
                    context.SaveChanges();

                    Subscription currentSubscription = AcademySystemDAO.GetSubscription(subscription.SubscriptionID);
                    Assert.IsTrue(AreEqual(subscription, currentSubscription));

                    context.Delete(subscription);
                    context.SaveChanges();

                    currentSubscription = AcademySystemDAO.GetSubscription(subscription.SubscriptionID);
                    Assert.IsNull(currentSubscription);
                }
            }
        }

        [TestMethod()]
        public void AddSubscriptionTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };
                    context.Add(course);
                    context.SaveChanges();

                    Subscription currentSubscription = AcademySystemDAO.AddSubscription((Guid)userIDs[i], course.CourseID);
                    Subscription subscription = context.Subscriptions.
                        SingleOrDefault(s => s.SubscriptionID == currentSubscription.SubscriptionID);
                    Assert.IsTrue(AreEqual(subscription, currentSubscription));

                    context.Delete(subscription);
                    context.SaveChanges();
                    currentSubscription = context.Subscriptions.
                        SingleOrDefault(s => s.SubscriptionID == subscription.SubscriptionID);
                    Assert.IsNull(currentSubscription);
                }
            }
        }

        [TestMethod()]
        public void DeleteSubscriptionTest()
        {
            using (AcademySystemEntities context = new AcademySystemEntities())
            {
                for (int i = 0; i < userIDs.Count; i++)
                {
                    Course course = new Course()
                    {
                        Title = "title" + i,
                        CreatorUserID = (Guid)userIDs[i],
                        StartDate = DateTime.Now,
                        EndDate = DateTime.Now.AddDays(i),
                        Information = "this is " + i + "th test course"
                    };
                    context.Add(course);
                    context.SaveChanges();

                    Subscription subscription = new Subscription()
                    {
                        CourseID = course.CourseID,
                        SubscriborUserID = (Guid)userIDs[i],
                        SubscriptionDate = DateTime.Now
                    };
                    context.Add(subscription);
                    context.SaveChanges();

                    bool isDeleted = AcademySystemDAO.DeleteSubscription(subscription.SubscriptionID);
                    Assert.IsTrue(isDeleted);

                    isDeleted = AcademySystemDAO.DeleteSubscription(subscription.SubscriptionID);
                    Assert.IsFalse(isDeleted);
                }
            }
        }

        private static void CloneDatabase()
        {
            string sqlCloneProductionDB = String.Format(@"
				USE master

				BACKUP DATABASE {0} TO DISK='{1}\{0}.bak'

				IF EXISTS(SELECT * FROM sys.sysdatabases where name='{2}')
				BEGIN
					ALTER DATABASE {2} SET SINGLE_USER WITH ROLLBACK IMMEDIATE
					DROP DATABASE {2}
				END

				RESTORE DATABASE {2} FROM DISK='{1}\{0}.bak'
				WITH
				MOVE '{0}' TO '{1}\{2}.mdf',
				MOVE '{0}_log' TO '{1}\{2}_Log.ldf'
			", PRODUCTION_DB_NAME, databaseDirectory, TEST_DB_NAME);

            SqlConnection dbConnection = new SqlConnection(CLONE_CONNECTION_STRING);
            dbConnection.Open();
            using (dbConnection)
            {
                SqlCommand cloneDBCmd = new SqlCommand(sqlCloneProductionDB, dbConnection);
                cloneDBCmd.ExecuteNonQuery();
            }
        }

        private static void PopulateTestingData()
        {
            userIDs = new List<object>();

            for (int i = 0; i < 128; i++)
            {
                string username = "testUser" + i;
                string password = username + "password";
                string email = username + "@" + password + ".com";

                MembershipUser newUser = Membership.CreateUser(username, password, email);
                userIDs.Add(newUser.ProviderUserKey);
            }
        }

        private static bool AreEqual(Course course1, Course course2)
        {
            if (course1.CourseID != course2.CourseID) return false;
            if (course1.CreatorUserID != course2.CreatorUserID) return false;
            if (course1.StartDate != course2.StartDate) return false;
            if (course1.EndDate != course2.EndDate) return false;
            if (course1.Information != course2.Information) return false;
            if (course1.Title != course2.Title) return false;
            return true;
        }

        private static bool AreEqual(Subscription subscription1, Subscription subscription2)
        {
            if (subscription1.CourseID != subscription2.CourseID) return false;
            if (subscription1.SubscriborUserID != subscription2.SubscriborUserID) return false;
            if (subscription1.SubscriptionDate != subscription2.SubscriptionDate) return false;
            if (subscription1.SubscriptionID != subscription2.SubscriptionID) return false;
            return true;
        }
    }
}
